package com.jjwxc.jwjskandriod.framework.http;

import android.net.TrafficStats;
import android.os.Process;
import android.os.SystemClock;
import android.webkit.WebSettings;

import com.alibaba.fastjson.JSON;
import com.google.gson.Gson;
import com.jjwxc.jwjskandriod.BuildConfig;
import com.jjwxc.jwjskandriod.config.YtApplication;
import com.jjwxc.jwjskandriod.framework.base.FFActivity;
import com.jjwxc.jwjskandriod.framework.base.FFResponseCode;
import com.jjwxc.jwjskandriod.framework.config.FFConfig;
import com.jjwxc.jwjskandriod.framework.utils.FFLogUtil;
import com.jjwxc.jwjskandriod.framework.utils.FFUtils;
import com.jjwxc.jwjskandriod.framework.utils.PreUtils;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.FileNameMap;
import java.net.URLConnection;
import java.util.ArrayList;
import java.util.Map;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

import okhttp3.Call;
import okhttp3.Callback;
import okhttp3.FormBody;
import okhttp3.Headers;
import okhttp3.MediaType;
import okhttp3.MultipartBody;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.RequestBody;
import okhttp3.Response;


public class FFNetWork {
    private static final String TAG = "FFNetWork";
    public static ArrayList<String> curlLogs = new ArrayList<>();
    public ExecutorService es1;
    FFActivity activity;

    public FFNetWork(FFActivity activity) {
        this.activity = activity;
        es1 = Executors.newFixedThreadPool(5);

    }  public FFNetWork() {
        es1 = Executors.newFixedThreadPool(5);

    }

    public <T> FFNetWorkRequest<T> get(String url, String words, FFNetWorkCallBack<T> callBack, Class<T> clazz,
                                       Object... params) {
        if (params != null && params.length % 2 == 1) {
            throw new RuntimeException("网络请求传入了单数个参数");
        }
        url = url + FFNetWorkUtils.getGetString(params);
        return new FFNetWorkRequest<>(this, words, url, callBack, clazz, null, null, false);
    }

    public <T> FFNetWorkRequest<T> get_synchronized(String url, String words, FFNetWorkCallBack<T> callBack, Class<T> clazz,
                                                    Object... params) {
        if (params != null && params.length % 2 == 1) {
            throw new RuntimeException("网络请求传入了单数个参数");
        }
        url = url + FFNetWorkUtils.getGetString(params);
        return new FFNetWorkRequest<>(this, words, url, callBack, clazz, null, null, true);

    }

    public <T> FFNetWorkRequest<T> post(String url, String words, FFNetWorkCallBack<T> callBack, Class<T> clazz,
                                        Object... param) {
        if (param != null && param.length % 2 == 1) {
            return new FFNetWorkRequest<>(this, words, url, callBack, clazz, null, param, false);
        } else {
            return new FFNetWorkRequest<>(this, words, url, callBack, clazz, null, param, false);
        }


    }


    public <T> FFNetWorkRequest<T> postFile(String url, String words, Map<String, File> files, FFNetWorkCallBack<T> callBack, Class<T> clazz,
                                            Object... param) {
        if (param != null && param.length % 2 == 1) {
            throw new RuntimeException("网络请求传入了单个文件");
        }
        return new FFNetWorkRequest<>(this, words, url, callBack, clazz, files, param, false);

    }

    public <T> FFNetWorkRequest<T> post_synchronized(String url, String words, FFNetWorkCallBack<T> callBack, Class<T> clazz,
                                                     Object... param) {
        if (param != null && param.length % 2 == 1) {
            throw new RuntimeException("网络请求传入了单数个参数");
        }
        return new FFNetWorkRequest<>(this, words, url, callBack, clazz, null, param, true);

    }

    public void downFile(final String url, final String destFileDir, final OnDownloadListener listener) {
        es1.submit(new Runnable() {
            public void run() {
                _downloadAsyn(url, destFileDir, listener);
            }
        });

    }

    public <T> void excuteHttp(FFNetWorkRequest<T> mRequest)
            throws IOException {
//        if (BuildConfig.DEBUG) {
        if (false) {
            boolean isGet = null == mRequest.getParams() && null == mRequest.getFiles();
            StringBuilder curl = new StringBuilder("curl -X ").append(isGet ? "'GET" : "'POST'").append(" \\\n");
            curl.append("'").append(mRequest.getUrl()).append("' \\\n");
            curl.append("-H 'accept: application/json' \\\n");
            if (!isGet) {
                //.post(requestBody).addHeader("device","android").addHeader("", .addHeader("User-Agent",
                //                                 getUserAgent()).build();
                curl.append("-H 'Content-Type: application/json' \\\n");
                curl.append("-H 'device: android' ");
                curl.append("-H 'Authorization: ").append(PreUtils.getString("Authorization", "")).append("' \\\n");
                if (null != mRequest.getParams() && mRequest.getParams().length > 0) {
                    String body = mRequest.getParams()[0].toString();
                    curl.append("-d '").append(body).append("' \\\n");
                }
            }
            curl = curl.delete(curl.length() - 3, curl.length());
            FFLogUtil.i("CURL", curl.toString());
            curlLogs.add(0, curl.toString());
            if (curlLogs.size() > 20) {
                curlLogs.remove(20);
            }
        }


        if (!FFUtils.checkNet()) {
            mRequest.setStatus(FFResponseCode.ERROR_NATIVE_NET_CLOST, "网络未连");
            return;
        }
        long r_o = TrafficStats.getUidRxBytes(Process.myUid());
        long t_o = TrafficStats.getUidTxBytes(Process.myUid());
        long startTime = SystemClock.currentThreadTimeMillis();
        OkHttpClient okHttpClient = new OkHttpClient();
        Request request;
        if (null == mRequest.getParams() && null == mRequest.getFiles()) {
            //get请求
            request = new Request.Builder().url(mRequest.getUrl()).removeHeader("User-Agent")
                    .addHeader("User-Agent", getUserAgent())
                    .addHeader("device","android")
                    .build();
        } else if (null != mRequest.getFiles()) {
            MultipartBody.Builder builder = new MultipartBody.Builder().setType(MultipartBody.FORM);
            if (mRequest.getParams() != null) {
                StringBuilder log = new StringBuilder();
                for (int i = 0; i < mRequest.getParams().length; i++) {
                    builder.addFormDataPart(mRequest.getParams()[i].toString(), mRequest.getParams()[i + 1].toString());
                    log.append("{").append(mRequest.getParams()[i]).append(":").append(mRequest.getParams()[i + 1].toString()).append("}");
                    i++;
                }
                FFLogUtil.e("文件参数：", log.toString());
            }

            for (Map.Entry<String, File> entry : mRequest.getFiles().entrySet()) {
                File file = entry.getValue();
                String fileName = file.getName();
                RequestBody requestBody = RequestBody.create(MediaType.parse(guessMimeType(fileName)), file);
                builder.addPart(Headers.of("Content-Disposition",
                                "form-data; name=\"" + entry.getKey() + "\"; filename=\"" + fileName + "\""),
                        requestBody);//“f”:是key值
                FFLogUtil.e("上传文件：", fileName);
            }
            RequestBody requestBody = builder.build();
            request = new Request.Builder()
                    .url(mRequest.getUrl())
                    .post(requestBody)
                    .build();

        } else {

            StringBuilder post = new StringBuilder();

            RequestBody requestBody;
            if (mRequest.getParams().length % 2 == 1) {
                MediaType JSON = MediaType.parse("application/json; charset=utf-8");
                post.append(mRequest.getParams()[0]);
                requestBody = RequestBody.create(JSON, String.valueOf(mRequest.getParams()[0]));
                if (FFConfig.DEBUG) {
                    FFLogUtil.e("JSON", "请求体为JSON");
                }
            } else {
                // post请求
                FormBody.Builder builder = new FormBody.Builder();
                for (int i = 0; i < mRequest.getParams().length; i++) {
                    builder.add(mRequest.getParams()[i].toString(), mRequest.getParams()[i + 1].toString());
                    post.append("{").append(mRequest.getParams()[i]).append(":").append(mRequest.getParams()[i + 1].toString()).append("}");
                    i++;
                }
                requestBody = builder.build();
                if (FFConfig.DEBUG) {
                    FFLogUtil.e("正常", "请求体为正常通用");
                }
            }
            //判断请求头是不是为空
            if (PreUtils.getString("Authorization", null) == null) {
                request = new Request.Builder()
                        .url(mRequest.getUrl())
                        .post(requestBody).removeHeader("User-Agent")
                        .addHeader("device","android")
                        .addHeader("User-Agent",
                                getUserAgent()).build();
            } else {
                request = new Request.Builder()
                        .url(mRequest.getUrl())
                        .post(requestBody).addHeader("device","android").addHeader("Authorization", PreUtils.getString("Authorization", null)).addHeader("User-Agent",
                                getUserAgent()).build();
            }


            FFLogUtil.e("请求参数：", post.toString());
        }
        Call call = okHttpClient.newCall(request);
        Response response = call.execute();
        int responseCode = response.code();
        if (responseCode == 200) {
            T responseString;
            String string = response.body().string();
            if (FFConfig.DEBUG) {
                FFLogUtil.e("网络请求返回数据:", string);
                long r_n = TrafficStats.getUidRxBytes(Process.myUid()) - r_o;
                long t_n = TrafficStats.getUidTxBytes(Process.myUid()) - t_o;
                long useTime = SystemClock.currentThreadTimeMillis() - startTime;
                String msg = "本次请求使用流量使用情况\n上传:" + getBytes(t_n) + "\n下载:" + getBytes(r_n)
                        + "\n总计:" + getBytes(r_n + t_n) + "\n耗时:" + useTime + "毫秒";
                FFLogUtil.i(TAG, msg);
            }
            try {
                responseString = JSON.parseObject(string, mRequest.getClazz());
            } catch (Exception ex) {
                try {
                    // fastjson解析失敗重新解析，fastjson無法解析Bean嵌套問題
                    responseString = new Gson().fromJson(string, mRequest.getClazz());
                } catch (Exception e) {
                    FFLogUtil.e("服务器返回数据解析失败", e);
                    mRequest.setStatus(FFResponseCode.ERROR_ANALYSIS, "服务器返回数据解析失败");
                    return;
                }
            }

            mRequest.setStatus(FFResponseCode.SUCCESS, "请求成功：服务器");
            mRequest.setEntity(responseString);

        } else if (responseCode == 404) {
            mRequest.setStatus(FFResponseCode.ERROR_NET_404, "状态码404");
        } else if (responseCode == 505) {
            mRequest.setStatus(FFResponseCode.ERROR_SITE_505, "状态码505不能关注自己");
        } else if (responseCode == 400) {
            mRequest.setStatus(FFResponseCode.ERROR_NET_400, "状态码400请求参数错误");
        } else if (responseCode == 500) {
            mRequest.setStatus(FFResponseCode.ERROR_NET_500, "状态码500远程服务器连接失败");
        } else if (responseCode == 403) {
            mRequest.setStatus(FFResponseCode.ERROR_NET_403, "状态码403服务器内部错误");
        } else {
            mRequest.setStatus(FFResponseCode.ERROR_SITE_XXX, "状态码"
                    + responseCode);
        }

    }

    private static String getUserAgent() {
        String userAgent = "";
        try {
            userAgent = WebSettings.getDefaultUserAgent(YtApplication.getInstance());
        } catch (Exception e) {
            userAgent = System.getProperty("http.agent");
        }
        StringBuffer sb = new StringBuffer();
        for (int i = 0, length = userAgent.length(); i < length; i++) {
            char c = userAgent.charAt(i);
            if (c <= '\u001f' || c >= '\u007f') {
                sb.append(String.format("\\u%04x", (int) c));
            } else {
                sb.append(c);
            }
        }
        FFLogUtil.e("User-Agent", sb.toString());
        return sb.toString();
    }

    /**
     * 异步下载文件
     *
     * @param destFileDir 本地文件存储的文件夹
     */
    private static void _downloadAsyn(final String url, final String destFileDir, final OnDownloadListener listener) {
        final Request request = new Request.Builder()
                .url(url)
                .build();
        final Call call = new OkHttpClient().newCall(request);
        call.enqueue(new Callback() {
            @Override
            public void onFailure(Call call, IOException e) {
                listener.onDownloadFailed(e.toString());
            }

            @Override
            public void onResponse(Call call, Response response) {
                InputStream is = null;
                byte[] buf = new byte[4096];
                int len;
                FileOutputStream fos = null;
                try {
                    assert response.body() != null;
                    is = response.body().byteStream();
                    long total = response.body().contentLength();
                    File file = new File(destFileDir);
                    fos = new FileOutputStream(file);
                    long sum = 0;
                    while ((len = is.read(buf)) != -1) {
                        fos.write(buf, 0, len);
                        sum += len;
                        int progress = (int) (sum * 1.0f / total * 100);
                        listener.onDownloading(progress);
                        listener.onDownloading(len, sum, total);
                    }
                    fos.flush();
                    // 下载完成
                    listener.onDownloadSuccess();
                    //如果下载文件成功，第一个参数为文件的绝对路径
                } catch (IOException e) {
                    listener.onDownloadFailed(e.toString());
                } finally {
                    try {
                        if (is != null) {
                            is.close();
                        }
                        if (fos != null) {
                            fos.close();
                        }
                    } catch (IOException ignored) {
                    }

                }

            }
        });
    }

    private void stopAll() {
        es1.shutdown();
    }

    private String guessMimeType(String path) {
        FileNameMap fileNameMap = URLConnection.getFileNameMap();
        String contentTypeFor = fileNameMap.getContentTypeFor(path);
        if (contentTypeFor == null) {
            contentTypeFor = "application/octet-stream";
        }
        return contentTypeFor;
    }

    private static String getBytes(long byteSum) {
        if (byteSum < 1024) {
            return byteSum + "字节";
        }
        if (byteSum < 1024 * 1024) {
            return FFUtils.getSubFloat(byteSum / 1024f) + "K";
        }
        if (byteSum < 1024 * 1024 * 1024) {
            return FFUtils.getSubFloat(byteSum / 1024f / 1024f) + "M";
        }
        return byteSum + "字节";
    }


    /**
     * 当前对象
     */
    public void onDestory() {
        stopAll();
    }

    /**
     * 本实例所对应的Activity是否已经finished
     */
    public boolean getDestroyed() {
        if (activity == null) {
            return false;
        }
        return activity.getDestroyed();
    }

    public interface OnDownloadListener {
        /**
         * 下载成功
         */
        void onDownloadSuccess();

        /**
         * @param progress 下载进度
         */
        void onDownloading(int progress);


        void onDownloading(long complete, long progress, long all);

        /**
         * 下载失败
         */
        void onDownloadFailed(String err);
    }
}
